package com.xinQing.blogme.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.PageInfo;
import com.xinQing.blogme.conf.annotation.CachedFunc;
import com.xinQing.blogme.constant.CacheConstant;
import com.xinQing.blogme.entity.Entity;
import com.xinQing.blogme.service.BaseService;
import com.xinQing.blogme.util.Check;
import com.xinQing.blogme.util.MyMapper;
import com.xinQing.blogme.util.Pageable;
import lombok.Getter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.transaction.annotation.Transactional;
import tk.mybatis.mapper.entity.Example;

import javax.annotation.PostConstruct;
import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 对BaseService基本业务逻辑的实现
 *
 * Created by null on 2017/3/8.
 */
public abstract class BaseServiceImpl<T extends Entity, ID extends Serializable> implements BaseService<T, ID> {

    @Autowired
    private MyMapper<T> mapper;

    private Class<T> entityClass;

    @Autowired
    @Getter
    private StringRedisTemplate stringRedisTemplate;

    @Getter
    private ValueOperations<String, String> opsForValue;

    public BaseServiceImpl() {
        // 获取实体的泛型信息
        Type genericType = getClass().getGenericSuperclass();
        Type[] params = ((ParameterizedType) genericType).getActualTypeArguments();
        entityClass = (Class) params[0];
    }

    @Override
    @Transactional
    @CachedFunc
    public int insert(T t) {
        if (t.getCreateTime() == null) {
            t.setCreateTime(new Date());
        }
        if (t.getUpdateTime() == null) {
            t.setUpdateTime(new Date());
        }
        int result = mapper.insert(t);
        setToCache(t);
        return result;
    }

    @Override
    @Transactional
    @CachedFunc
    public int insertList(List<T> list) {
        list.stream().map(entity -> {
            if (entity.getCreateTime() == null) {
                entity.setCreateTime(new Date());
            }
            if (entity.getUpdateTime() == null) {
                entity.setUpdateTime(new Date());
            }
            return entity;
        });
        int result = mapper.insertList(list);
        list.forEach(t -> setToCache(t));
        return result;
    }

    @Override
    @Transactional(readOnly = true)
    public List<T> selectMany(T t) {
        return mapper.select(t);
    }

    @Override
    @Transactional(readOnly = true)
    public T selectOne(T t) {
        return mapper.selectOne(t);
    }
    
    @Override
    @Transactional(readOnly = true)
    @CachedFunc
    public T select(ID id) {
        String cachedJSONString = opsForValue.get(getCacheKeyPrefixOfId() + id);
        if (Check.isEmpty(cachedJSONString)) {
            T t = mapper.selectByPrimaryKey(id);
            setToCache(t);
            return t;
        }
        return getByCachedJSONString(cachedJSONString);
    }

    @Override
    @Transactional(readOnly = true)
    public List<T> selectAll() {
        return mapper.selectAll();
    }

    @Override
    @Transactional(readOnly = true)
    public List<T> select(Example example) {
        return mapper.selectByExample(example);
    }

    @Override
    @Transactional
    @CachedFunc
    public int update(T t) {
        if (t.getUpdateTime() == null) {
            t.setUpdateTime(new Date());
        }
        int result = mapper.updateByPrimaryKey(t);
        setToCache(t);
        return result;
    }

    @Override
    @Transactional
    @CachedFunc
    public int remove(ID id) {
        int result = mapper.deleteByPrimaryKey(id);
        removeFromCache(id);
        return result;
    }

    @Override
    @Transactional(readOnly = true)
    public PageInfo<T> select(T t, Pageable pageable) {
        // 调用该方法，紧挨着的查询方法会分页查询(sql加上limit n, m)
        pageable.startPage();
        if (Check.isNull(t)) {
            return pageable.ofPage(selectAll());
        }
        return pageable.ofPage(selectMany(t));
    }

    @PostConstruct
    public void postConstruct() {
        this.opsForValue = stringRedisTemplate.opsForValue();
    }

    protected void setToCache(T t) {
        if (Check.isNotNull(t)) {
            opsForValue.set(getCacheKeyPrefixOfId() + t.getId(), JSONObject.toJSONString(t),
                    CacheConstant.EXPIRE_TIME, CacheConstant.EXPIRE_TIME_UNIT);
        }
    }

    protected void removeFromCache(ID id) {
        stringRedisTemplate.expire((String) id, 5, TimeUnit.SECONDS);
    }

    protected T getByCachedJSONString(String cachedJSONString) {
        return JSONObject.parseObject(cachedJSONString, entityClass);
    }

}